package com.cdelink.xzgy.ftp.user.service.impl;

import com.cdelink.xzgy.ftp.common.Constants;
import com.cdelink.xzgy.ftp.common.ReturnData;
import com.cdelink.xzgy.ftp.exception.BaseException;
import com.cdelink.xzgy.ftp.exception.InvalidDataException;
import com.cdelink.xzgy.ftp.user.entity.Records;
import com.cdelink.xzgy.ftp.user.entity.dto.FileDto;
import com.cdelink.xzgy.ftp.user.entity.vo.FileVo;
import com.cdelink.xzgy.ftp.user.mapper.one.RecordsMapper;
import com.cdelink.xzgy.ftp.user.service.FileService;
import com.cdelink.xzgy.ftp.util.FileConvertUtil;
import com.cdelink.xzgy.ftp.util.FtpUtil;
import com.cdelink.xzgy.ftp.util.ZipUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * (User)表服务实现类
 *
 * @author sxl
 * @since 2021-12-13 17:14:25
 */
@Service
public class FileServiceImpl implements FileService {

    //public static Map<String, Object> map = new ConcurrentHashMap<>();
    //public static Queue<HttpServletResponse> queue = new LinkedList<HttpServletResponse>();


    @Autowired
    FtpUtil ftpUtil;

    @Autowired
    RecordsMapper recordsMapper;

    @Autowired
    RestTemplate restTemplate;

    @Value("${ftp.nginx:http://192.168.0.2:9999/resource/}")
    String nginxServer;
    String pattern = "^[^/\\\\\\\\:\\\\*\\\\?\\\\&\\\\<\\\\>\\\\|\\\"]{1,255}$";
    String directory_pattern = "^[^\\\\\\\\:\\\\*\\\\?\\\\&\\\\<\\\\>\\\\|\\\"]{1,255}$";
    final int FOLDER_MAX = 50;
    final int FILE_MAX = 20;

    @Override
    public List<FileVo> listFiles(String folder) {
        if (StringUtils.isNotBlank(folder) && !folder.startsWith("/")) {
            folder = "/" + folder;
        }
        return ftpUtil.buildFileVo(folder);
    }

    @Override
    public ReturnData uploadFile(MultipartFile file, String folder, Integer insertOrCover, boolean exist, String userId, String taskId) {
        if (insertOrCover == null) {
            throw new InvalidDataException("未指定状态");
        }
        if (insertOrCover != Constants.FILE_REPEAT && insertOrCover != Constants.FILE_INSERT) {
            throw new InvalidDataException("状态参数不合法");
        }
        if (StringUtils.isBlank(folder)) {
            throw new InvalidDataException("未指定文件夹");
        }
        if (StringUtils.isBlank(taskId)) {
            throw new InvalidDataException("任务ID不能为空");
        }
        String originalFilename = file.getOriginalFilename();
        int index = originalFilename.lastIndexOf(".");
        if (index == -1) {
            throw new InvalidDataException("未知的文件格式");
        }
        String name = originalFilename.substring(0, index);
//        if (name.length() > FILE_MAX) {
//            throw new InvalidDataException("文件名不能超过20个字符");
//        }

        if (!Pattern.matches(directory_pattern, name)) {
            throw new InvalidDataException("文件命名不符合规范，不能包含以下特殊字符 & < > / \\ | : * ? 、\" ");
        }
        long begin = System.currentTimeMillis();
        try {
            String fullPath = ftpUtil.uploadToFtpFromServletNew(file, folder, insertOrCover, exist, taskId);
            if (fullPath != null) {
                long end = System.currentTimeMillis();
                System.err.println(taskId + "上传任务结束，共耗时：[" + (end - begin) + "]毫秒");
                String replace = fullPath.replace(nginxServer, "");
                if (!replace.startsWith("/")) {
                    replace = "/" + replace;
                }
                RecordServiceImpl.insert(userId, insertOrCover == 1 ? "替换" : "上传", replace, false, true);
                return ReturnData.ok("上传成功").setData(replace);
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("文件上传失败");
            return ReturnData.fail(e.getMessage());
        }
        return ReturnData.fail("上传失败");
    }

    @Override
    public boolean checkRepeat(String folder, String fileName) {
        if (StringUtils.isBlank(folder)) {
            throw new BaseException("未指定文件夹");
        }
        if (StringUtils.isBlank(fileName) && !fileName.contains(".")) {
            throw new InvalidDataException("未知的文件格式");
        }
        List<FTPFile> ftpFiles = ftpUtil.listFilesByFolder(folder);
        if (CollectionUtils.isEmpty(ftpFiles)) {
            return false;
        }
        List<String> fruitName = ftpFiles.stream().filter(f -> !f.isDirectory()).map((FTPFile::getName)).collect(Collectors.toList());
        if (fruitName.contains(fileName)) {
            return true;
        }
        return false;
    }

    @Override
    public FileVo buildDirectory(String folder) {
        if (StringUtils.isBlank(folder)) {
            throw new InvalidDataException("文件夹名不能为空");
        }
        int index = folder.lastIndexOf("/");
        String name = folder.substring(index + 1);
        if (name.length() > FOLDER_MAX) {
            throw new InvalidDataException("文件夹命名不能超过16个字符");
        }
        if (!Pattern.matches(pattern, name)) {
            throw new InvalidDataException("文件夹命名不符合规范，不能包含以下特殊字符& < > / \\ | : * ? 、\" ");
        }
        if (!ftpUtil.buildDirectory(folder)) {
            throw new BaseException("操作失败");
        }
        List<FileVo> fileVos;
        if (index == 0) {
            fileVos = listFiles("");
        } else {
            fileVos = listFiles(folder.substring(0, index));
        }
        FileVo file = null;
        for (FileVo fileVo : fileVos) {
            if (fileVo.isDirectory() && fileVo.getName().equals(name)) {
                file = fileVo;
                break;
            }
        }
        return file;
    }

    @Override
    public boolean delDirectory(String folder) {
        if (StringUtils.isBlank(folder)) {
            throw new InvalidDataException("未指定文件夹");
        }
        if (!folder.endsWith("/")) {
            folder = folder + "/";
        }
        if (!folder.startsWith("/")) {
            folder = "/" + folder;
        }
        return ftpUtil.delDirectory(folder);
    }

    @Override
    public void download(String fileName, HttpServletResponse response) {
        ftpUtil.downloadFtpFile(fileName, response);
    }


    @Override
    public boolean delFile(String url) {
        if (StringUtils.isBlank(url)) {
            throw new InvalidDataException("无效的文件地址");
        }
        if (!url.startsWith("/")) {
            url = "/" + url;
        }
        return ftpUtil.delFile(url);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean renameFolder(String folder, String newFolder) {
        if (StringUtils.isBlank(folder) || StringUtils.isBlank(newFolder)) {
            throw new InvalidDataException("文件夹名/新文件夹名不能为空");
        }
        //新文件夹名与旧文件名一样  视为重命名成功
        if (newFolder.equals(folder)) {
            return true;
        }
        if (!folder.startsWith("/")) {
            folder = "/" + folder;
        }
        if (!newFolder.startsWith("/")) {
            newFolder = "/" + newFolder;
        }
        int index = newFolder.lastIndexOf("/");
        String newName = newFolder.substring(index + 1);
        if (newName.length() > FOLDER_MAX) {
            throw new InvalidDataException("文件夹名不能超过16个字符");
        }
        if (!Pattern.matches(pattern, newName)) {
            throw new InvalidDataException("文件夹命名不符合规范，不能包含以下字符& < > / \\ | : * ? 、\"");
        }
//        String path = newFolder.substring(0, index);
//        List<FTPFile> ftpFiles = ftpUtil.listFilesByFolder(path);
//        if (ftpFiles.size() > 0) {
//            List<String> names = ftpFiles.stream().map(FTPFile::getName).collect(Collectors.toList());
//            if (names.contains(newName)) {
//                throw new InvalidDataException("当前文件夹名已经存在重复，请重新输入");
//            }
//        }
        boolean rename = ftpUtil.rename(folder, newFolder);
        if (rename) {
            //重命名成功后 更新文件操作记录
            updateHistory(folder, newFolder);
            return true;
        } else {
            throw new BaseException("当前文件夹名已有重复，请重新输入");
        }
    }

    @Override
    public List<Records> getFileHistory(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            throw new InvalidDataException("文件名格式不合法");
        }
        List<Records> records = recordsMapper.getFileHistory(fileName);
        return records;
    }

    @Override
    public FileDto getFileList(String folder) {
        FileDto fileDto = ftpUtil.getFileList(folder);
        return fileDto;
    }

    @Override
    public boolean stopProcess(String code) {
        if (StringUtils.isBlank(code)) {
            throw new InvalidDataException("taskID不能为空");
        }
        return ftpUtil.distinctFtpClient(code);
    }

    @Override
    public String getTaskId(String userId) {
        long l = System.currentTimeMillis();
        String random = UUID.randomUUID().toString().replace("_", "");
        String key = l + ":" + userId + "_" + random;
        FtpUtil.map.put(key, key);
        return key;
    }

    @Override
    public void onlinePreview(String url, HttpServletResponse response) throws Exception {
        OutputStream outputStream = null;
        InputStream in = null;
        try {
            //获取文件类型
            int lastIndex = url.lastIndexOf(".");
            if (lastIndex == -1) {
                throw new Exception("文件格式不正确");
            }
            String suffix = url.substring(lastIndex + 1);
            if (!Arrays.asList(Constants.PIC_SUFFIX).contains(suffix) && !Arrays.asList(Constants.OFFICE_SUFFIX).contains(suffix)) {
                throw new InvalidDataException("当前文件格式不支持预览");
            }
            outputStream = response.getOutputStream();
            //网络文件还是ftp服务器资源
            if (!url.startsWith("http://")) {
                //ftp服务器资源
                //是否是pdf
                if ("pdf".equalsIgnoreCase(suffix)) {
                    ftpUtil.writeResponse(url, outputStream, true, suffix);
                    return;
                } else {
                    ftpUtil.writeResponse(url, outputStream, false, suffix);
                    return;
                }
            } else {
                //网络文件
                //是否是pdf
                if ("pdf".equalsIgnoreCase(suffix)) {
                    in = FileConvertUtil.convertNetFile(url, true, suffix);
                } else {
                    in = FileConvertUtil.convertNetFile(url, false, suffix);
                }
            }
            if (in == null) {
                throw new InvalidDataException("文件不存在,请检查文件地址是否正确");
            }
            ftpUtil.subsectionWriteFile(outputStream, in);
        } catch (Exception e) {
            if (e instanceof InvalidDataException) {
                throw e;
            }
            e.printStackTrace();
        } finally {
            try {
                //关流
                if (outputStream != null) {
                    outputStream.flush();
                    outputStream.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * 重命名后更新历史记录中涉及到该文件夹的批量替换
     *
     * @param folder
     * @param newFolder
     */
    public void updateHistory(String folder, String newFolder) {
        int oldIndex = folder.lastIndexOf("/");
        int index = newFolder.lastIndexOf("/");
        String oldName = folder.substring(oldIndex + 1);
        String newName = newFolder.substring(index + 1);
        List<Records> records = recordsMapper.list("%" + oldName + "%");
        if (!CollectionUtils.isEmpty(records)) {
            List<Records> newRecords = new ArrayList<>();
            for (Records record : records) {
                Records newRecord = new Records();
                BeanUtils.copyProperties(record, newRecord);
                String folderOld = record.getFolder();
                if (folderOld.contains(oldName)) {
                    String oldFolder = record.getFolder();
                    String replace = oldFolder.replace(oldFolder, newName);
                    newRecord.setFolder(replace);
                }
                String fileName = record.getFileName();
                if (fileName.contains(oldName)) {
                    String replace = fileName.replace(oldName, newName);
                    newRecord.setFileName(replace);
                }
                newRecords.add(newRecord);
            }
            recordsMapper.insertOrUpdateBatch(newRecords);
        }
    }

    @Override
    public List<FileVo> searchByFileName(String fileName, String currentFolder) {
        if (StringUtils.isBlank(fileName)) {
            throw new InvalidDataException("搜索文件或文件名不能为空");
        }
        return ftpUtil.searchFile(fileName, currentFolder);
    }

    @Override
    public boolean moveFileOrDir(String fileOrDir, String targetFolder) {
        if (StringUtils.isBlank(fileOrDir)) {
            throw new InvalidDataException("文件/文件夹名不能为空");
        }
        if (StringUtils.isBlank(targetFolder)) {
            throw new InvalidDataException("目标文件夹不能为空");
        }
        boolean isDirectory = false;
        if (fileOrDir.lastIndexOf(".") == -1) {
            isDirectory = true;
        }
        return ftpUtil.moveFileOrDir(fileOrDir, targetFolder, isDirectory);
    }

    @Override
    public void downloadZip(String dir, HttpServletResponse response) {
        String name = Thread.currentThread().getName();
        if (StringUtils.isBlank(dir)) {
            throw new InvalidDataException("未指定文件或文件夹");
        }
//        if (queue.size() != 0) {
//            System.err.println("线程： " + name + " 未执行" + " 下载文件夹" + dir);
//            throw new BaseException(1, "当前请求排序" + queue.size() + "正在开始处理中请你耐心等待");
//        }
        //文件还是文件夹
//        if (dir.contains(".")) {
//            //文件下载
//            map.put(key, dir);
//            System.out.println("进入下载" + dir + "下载程序中");
//            download(dir, response);
//            return;
//        }
        int index = dir.lastIndexOf("/");
        if (index == -1) {
            throw new InvalidDataException("未知路径");
        }
        long time = System.currentTimeMillis();
        // String key = time + "_" + userId;
//        queue.add(response);
        System.err.println("线程： " + name + " 开始执行" + " 下载文件夹" + dir);
        String downLoadName = "";
        if (index == 0) {
            //根目录
            downLoadName = downLoadName + dir.replace("/", "") + ".zip";
        } else {
            //非根目录
            downLoadName = downLoadName + dir.substring(index + 1) + ".zip";
        }
        //将文件进行打包下载
        OutputStream out = null;
        try {
            ftpUtil.createZip(dir, response, downLoadName);
        } catch (Exception e) {
            if (e instanceof InvalidDataException) {
                throw e;
            } else {
                e.printStackTrace();
            }
        } finally {
            try {
                if (out != null) {
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //queue.remove(response);
            System.err.println("线程： " + name + " 执行下载" + dir + "文件夹完毕 移除map任务key");
        }

    }


    @Override
    public void downloadDirectory(String dir, HttpServletResponse response) {
        if (StringUtils.isBlank(dir)) {
            throw new InvalidDataException("未指定文件或文件夹");
        }
        int index = dir.lastIndexOf("/");
        if (index == -1) {
            throw new InvalidDataException("未知路径");
        }
        ftpUtil.createZip(dir, response);
    }

    @Override
    public ReturnData createLocalDir(String dir, String targetDir) {
        if (StringUtils.isBlank(dir)) {
            throw new InvalidDataException("未指定目录");
        }
        if (StringUtils.isBlank(targetDir)) {
            throw new InvalidDataException("未指定目标路径");
        }
//        if (targetDir.endsWith(":/") || targetDir.endsWith("：\\")) {
//            throw new InvalidDataException("路径不合法");
//        }
        ResponseEntity<Resource> res = restTemplate.getForEntity("http://192.168.0.141:6003/directoryInput?dir=" + dir, Resource.class);
        try {
            if (!res.getStatusCode().equals(HttpStatus.OK)) {
                throw new BaseException(res.getStatusCode().value(), "远程调用失败");
            }
            InputStream in = Objects.requireNonNull(res.getBody()).getInputStream();
            if (in != null) {
                ZipUtils.decompressionFile(in, targetDir);
                return ReturnData.ok("下载文件夹成功");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ReturnData.ok("操作失败");
    }

}
